package cn.seamus.marksman.http;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.util.Assert;

import cn.seamus.marksman.common.Constants;
import cn.seamus.marksman.common.MarksmanException;

public abstract class AbstractHttpExecuter implements IHttpExecuter {
    private static Log logger = LogFactory.getLog(AbstractHttpExecuter.class);

    protected ThreadLocal<CloseableHttpClient> clientLocal = new ThreadLocal<CloseableHttpClient>();

    protected abstract CloseableHttpClient retrieveHttpClient();

    protected abstract HttpHost createHttpHost(String host, int port, String protocl);

    protected abstract HttpGet createHttpGet(String path);

    protected abstract HttpPost createHttpPost(String path);

    /*
     * (non-Javadoc)
     * 
     * @see cn.seamus.marksman.http.IHttpExecuter#close()
     */
    @Override
    public void close() {
        logger.info("[Close client]starting...");
        CloseableHttpClient httpClient = clientLocal.get();
        if (httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                logger.error(e);
            }
        }
        logger.info("[Close client]done.");
    }

    @Override
    public CloseableHttpResponse executeGet(URL url, Map<String, Object> params) throws MarksmanException {
        Assert.notNull(url);

        CloseableHttpClient httpClient = retrieveHttpClient();
        try {
            HttpHost target = createHttpHost(url.getHost(), url.getPort(), url.getProtocol());

            HttpGet httpGet = createHttpGet(url.getPath());
            CloseableHttpResponse response = httpClient.execute(target, httpGet);

            logger.info("[Response status]" + response.getStatusLine());

            return response;
        } catch (ClientProtocolException e) {
            throw new MarksmanException(e);
        } catch (IOException e) {
            throw new MarksmanException(e);
        }
    }

    @Override
    public CloseableHttpResponse executePost(URL url, Map<String, Object> params) throws MarksmanException {
        Assert.notNull(url);

        CloseableHttpClient httpClient = retrieveHttpClient();
        try {
            HttpHost target = createHttpHost(url.getHost(), url.getPort(), url.getProtocol());

            HttpPost httpPost = createHttpPost(url.getPath());

            Header[] cookies = (Header[]) params.get(Constants.MAP_KEY_COOKIES);
            if (cookies != null) {
                for (Header cookie : cookies) {
                    httpPost.addHeader(cookie);
                }
            }
            params.remove(Constants.MAP_KEY_COOKIES);

            List<NameValuePair> formparams = new ArrayList<NameValuePair>();
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                formparams.add(new BasicNameValuePair(entry.getKey(), (String) entry.getValue()));
            }
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);

            httpPost.setEntity(entity);

            HttpClientContext context = HttpClientContext.create();

            CloseableHttpResponse response = httpClient.execute(target, httpPost, context);

            params.put(Constants.MAP_KEY_COOKIE, context.getCookieStore().getCookies());

            logger.info("[Response status]" + response.getStatusLine());

            return response;
        } catch (ClientProtocolException e) {
            throw new MarksmanException(e);
        } catch (IOException e) {
            throw new MarksmanException(e);
        }
    }
}
